package com.sinodata.bsm.center.engine.event;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

import com.sinodata.bsm.center.bean.EventRuleBean;
import com.sinodata.bsm.center.bean.PropertyBean;
import com.sinodata.bsm.center.bean.ResBean;
import com.sinodata.bsm.center.cache.EventTypeCache;
import com.sinodata.bsm.center.cache.NewEventCache;
import com.sinodata.bsm.center.cache.PropertyValueCache;
import com.sinodata.bsm.center.cache.ResCache;
import com.sinodata.bsm.center.service.event.EventService;
import com.sinodata.bsm.center.util.spring.SpringContextHolder;
import com.sinodata.bsm.common.constants.DataTypeConstants;
import com.sinodata.bsm.common.constants.EventConstants;
import com.sinodata.bsm.common.constants.SeparatorConstants;
import com.sinodata.bsm.common.utils.StringUtil;
import com.sinodata.bsm.common.vo.Event;
import com.sinodata.bsm.common.vo.Property;
import com.sinodata.bsm.common.vo.Res;

/**
 * <p>
 * Description: 表格事件可恢复事件规则组
 * </p>
 *
 * @author liulibo
 * @version 1.0

 * <p>
 * History: 
 *
 * Date                     Author         Version     Description
 * ---------------------------------------------------------------------------------
 * 2013-1-14 PM 2:16:38     liulibo        1.0         To create
 * </p>
 *
 * @since 
 * @see     
 */
public class TableResumableRuleGroup extends EventRuleGroup {

    protected static Logger logger = Logger.getLogger(ResumableRuleGroup.class);

    private static NewEventCache newEventCache = SpringContextHolder.getBean(NewEventCache.class);

    private static EventService eventService = SpringContextHolder.getBean(EventService.class);

    private static EventTypeCache eventTypeCache = SpringContextHolder.getBean(EventTypeCache.class);

    private static PropertyValueCache propertyValueCache = SpringContextHolder.getBean(PropertyValueCache.class);

    private static ResCache resCache = SpringContextHolder.getBean(ResCache.class);

    private static String regex = "(tprop\\(\\d+,\\d+[L|l]?,)([^\\)]+)(\\))";

    /**
     * @param resId
     * @param eventTypeId
     */
    public TableResumableRuleGroup(Long resId, Long eventTypeId) {
        super(resId, eventTypeId);
    }

    @Override
    public List<Event> matches(Long resId, Long propId) {
        //----------------------
        //新思路匹配事件
        //拿规则匹配全部事件，作为临时事件。
        //0.规则不存在了，修改规则，则清除这个规则的所有事件。
        //1.如果与原事件重复则更新次数。
        //2.如果在原事件中不存在，则产生故障事件。
        //3.如果原事件存在的，没有再产生，则产生正常事件（恢复事件）。

        List<Event> events = new ArrayList<Event>();
        Property property = PropertyBean.get(propId).property();
        int dataTypeId = property.getDataTypeId().intValue();
        String format = property.getFormat();
        if (dataTypeId != DataTypeConstants.DATA_TYPE_OBJECT && format != null) {//是表格类型数据
            return events;
        }
        if (rules == null || rules.size() == 0) {
            events.add(this.makeClearEvent());
        }
        Map<String, Event> map = new HashMap<String, Event>();//临时事件Map集合
        Set<String> prepareResumbleObjectId = new HashSet<String>();
        Map<String, String> nxyResumble = new HashMap<String, String>();
        String result = propertyValueCache.getValue(resId, propId);
        Set<String> nxyAllObjectId = new HashSet<String>();
        for (Iterator<EventRuleBean> i = rules.iterator(); i.hasNext();) {
            EventRuleBean r = i.next();
            String expr = r.eventRule().getExpr(); //!tprop(8173,120201090000004L,主机用户).equals("oracle")
            String msg = r.eventRule().getMsg(); //"用户："+tprop(8173,120201090000004L,主机用户)+" 非法登陆数据库"
            int nxyKeyCol = nxyGetKeyCol(expr, format.split("\\|"));
            Map<String, String> exprMap = StringUtil.getAllByRegex(expr, regex);
            Map<String, String> msgMap = StringUtil.getAllByRegex(msg, regex);

            String keyWord = r.eventRule().getObjectExpr();
            String[] keyWords = null;
            if (keyWord == null || "".equals(keyWord.trim())) {
                String[] formats = format.split("|");
                keyWords = new String[formats.length];
                for (int j = 0; j < formats.length; j++) {
                    keyWords[j] = j + "";
                }
            } else {
                keyWords = keyWord.trim().split(",");
            }
            if (expr == null || msg == null || "".equals(expr) || "".equals(msg)) {
                break;
            }

            String[] values = result.split(SeparatorConstants.LIN_SEPARATOR);
            for (int v = 0; v < values.length; v++) {
                String[] dataArray = values[v].split(SeparatorConstants.COL_SEPARATOR);
                String nxyKeyValue = dataArray[nxyKeyCol];
                r.eventRule().setExpr(build(exprMap, v));
                r.eventRule().setMsg(build(msgMap, v));
                String objectId = resId + "_" + propId + "_";
                for (int j = 0; j < keyWords.length; j++) {
                    int keyCol = Integer.parseInt(keyWords[j].trim());
                    objectId += dataArray[keyCol] + "_";
                }
                nxyAllObjectId.add(objectId);
                if (r.matching()) {
                    try {
                        Event newEvent = r.makeEvent(objectId);
                        Event hevent = map.get(objectId);
                        if (hevent == null || hevent.getLevel() < newEvent.getLevel()) {
                            map.put(objectId, newEvent);
                        }
                    } catch (Exception e) {
                        logger.error("failed to create event ", e);
                    }
                } else {
                    //恢复对应的事件
                    String key = r.eventType().getId() + SeparatorConstants.COL_SEPARATOR + objectId;
                    prepareResumbleObjectId.add(key);
                    nxyResumble.put(key, nxyKeyValue);
                }
                r.eventRule().setExpr(expr);
                r.eventRule().setMsg(msg);
            }
        }

        /**
         * 恢复事件
         */
        List<Event> newEvetList = newEventCache.getNewEventsByResId(resId) == null ? new ArrayList<Event>() : newEventCache.getNewEventsByResId(resId);
        /**
         * 农信银saf产生的新事件的objectId在
         * 本次采集中找不到对应的数据的这类事件也要恢复
         */
        for (int i = 0; resId.equals(4600L) && i < newEvetList.size(); i++) {
            String objectId = newEvetList.get(i).getObjectId();
            if (!nxyAllObjectId.contains(objectId)) {
                events.add(this.makeResumeEvent(newEvetList.get(i).getLevel(), EventConstants.LEVEL_NORMAL, objectId));
            }
        }

        for (String key : prepareResumbleObjectId) {
            Res res = resCache.get(resId);
            if (res.getInternalId() != null && res.getInternalId().startsWith("SAF")) {
                /**
                 * 针对农信银的多级告警特殊处理的
                 * 只有值小于10才恢复
                 */
                int nxyKeyValueInt = Integer.parseInt(nxyResumble.get(key));
                if (nxyKeyValueInt < 10) {
                    String[] eventTypeIdAndobjectId = key.split(SeparatorConstants.COL_SEPARATOR);
                    Long eventTypeId = Long.parseLong(eventTypeIdAndobjectId[0]);
                    String objectId = eventTypeIdAndobjectId[1];
                    Event event = newEventCache.getEventByObjectId(objectId, eventTypeId);
                    if (event != null) {
                        events.add(this.makeResumeEvent(event.getLevel(), EventConstants.LEVEL_NORMAL, objectId));
                    }
                }
            } else {
                String[] eventTypeIdAndobjectId = key.split(SeparatorConstants.COL_SEPARATOR);
                Long eventTypeId = Long.parseLong(eventTypeIdAndobjectId[0]);
                String objectId = eventTypeIdAndobjectId[1];
                Event event = newEventCache.getEventByObjectId(objectId, eventTypeId);
                if (event != null) {
                    events.add(this.makeResumeEvent(event.getLevel(), EventConstants.LEVEL_NORMAL, objectId));
                }
            }
        }
        if (map.size() == 0) {
            return events;
        }

        List<Event> newEvents = new ArrayList<Event>(map.size());
        for (String key : map.keySet()) {
            newEvents.add(map.get(key));
        }

        for (Event event : newEvents) {
            String objectId = event.getObjectId();
            Event tmp = newEventCache.getEventByObjectId(objectId, event.getEventTypeId());
            if (tmp == null) {//新的里面没有这个对象ID的事件
                events.add(event);
            } else if (tmp.getLevel().intValue() == event.getLevel()) { //事件压制，更新产生次数
                int cnt = tmp.getCount();
                if (cnt <= 0) {
                    tmp.setCount(1);
                    tmp.setLastOccurTime(event.getOccurTime());
                } else {
                    tmp.setCount(cnt + 1); //同一引用次处已更新缓存
                    tmp.setLastOccurTime(new Date());
                }
                //更新事件个数
                eventService.update(tmp);
            } else if (tmp.getLevel() > event.getLevel()) { //故障升级 产生恢复到正常的事件，再产生新的故障事件
                events.add(this.makeResumeEvent(event.getLevel(), EventConstants.LEVEL_NORMAL, objectId));
                events.add(tmp);
            } else if (tmp.getLevel() < event.getLevel()) { //故障降级 产生恢复事件和新的故障事件
                events.add(this.makeResumeEvent(event.getLevel(), tmp.getLevel(), objectId));
                events.add(tmp);
            }
        }
        return events;
    }

    /**
     * 产生一个正常事件
     * 
     * @return
     */
    private Event makeResumeEvent(int beforeLevel, int afterLevel, String objectId) {
        Event event = new Event();
        event.setResId(resId);
        event.setEventTypeId(eventTypeId);
        event.setLevel(beforeLevel);
        StringBuilder msg = new StringBuilder();
        if (ResBean.get(resId).res().getIp() != null) {
            msg.append(ResBean.get(resId).res().getIp());
            msg.append(" ");
        }
        msg.append(ResBean.get(resId).res().getName());
        msg.append(" ");
        msg.append(eventTypeCache.get(eventTypeId).getName());
        msg.append("事件由");
        msg.append(EventConstants.getLevelDesc(beforeLevel));
        msg.append("恢复到");
        msg.append(EventConstants.getLevelDesc(afterLevel));
        if (objectId != null) {
            event.setObjectId(objectId);
        }
        event.setMsg(msg.toString());
        event.setOccurTime(new Date());
        event.setStatus(EventConstants.STATUS_CONFIRMED);
        event.setCatalog(EventConstants.CATALOG_REFAULTEVENT);
        return event;
    }

    /**
     * 因系统规则清除，而清除事件
     * 
     * @return
     */
    private Event makeClearEvent() {
        Event event = new Event();
        event.setResId(resId);
        event.setEventTypeId(eventTypeId);
        event.setLevel(-1);
        StringBuilder msg = new StringBuilder();
        if (ResBean.get(resId).res().getIp() != null) {
            msg.append(ResBean.get(resId).res().getIp());
            msg.append(" ");
        }
        msg.append(ResBean.get(resId).res().getName());
        msg.append(" ");
        msg.append(eventTypeCache.get(eventTypeId).getName());
        msg.append("事件因事件规则修改或删除被系统自动清除");
        event.setMsg(msg.toString());
        event.setOccurTime(new Date());
        event.setStatus(EventConstants.STATUS_CONFIRMED);
        event.setCatalog(EventConstants.CATALOG_RULE_CLEAR);
        return event;
    }

    /**
     * 
     * 这个方法的作用有两个：
     * 1为形如：tprop(25625,120201110000003L,状态)的表达式中的字符串”状态“加上双引号，
     * 2将table的行号keyCol加到后面
     * 组成形如：tprop(25625,120201110000003L,"状态",0)的表达式以便能调用com.sinodata.bsm.center.expression.Functions中的getTablePropertyValue方法
     * @param expr
     *            原始的表达式
     * @param keyCol
     *            table的行号
     * @return
     */
    private String buildSingleExpr(String expr, int keyCol) {
        Matcher matcher = Pattern.compile(regex).matcher(expr);
        StringBuffer buffer = new StringBuffer();
        if (matcher.find()) {
            buffer.append(matcher.group(1));
            buffer.append("\"");
            buffer.append(matcher.group(2));
            buffer.append("\"");
            buffer.append("," + keyCol);
            buffer.append(matcher.group(3));
        }
        String newExpr = buffer.toString();
        return newExpr.equals("") ? expr : newExpr;
    }

    private String build(Map<String, String> map, int keyCol) {
        String expression = map.get("sum");
        if (expression != null) {
            for (String key : map.keySet()) {
                expression = expression.replace(key, buildSingleExpr(map.get(key), keyCol));
            }
        }
        return expression;
    }

    /**
     * 从事件表达式中获取设置事件产生的行数
     * @param expr
     * @return
     */
    private int nxyGetKeyCol(String expr, String[] format) {
        String regex = "(tprop\\(\\d+,\\d+[L|l]?,)([^\\)]+)(\\)).*";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(expr);
        if (matcher.find()) {
            String keyCol = matcher.group(2);
            for (int i = 0; i < format.length; i++) {
                if (keyCol.contains(format[i].trim())) {
                    return i;
                }
            }
        }
        return 0;
    }
}
